﻿Imports System.Runtime.InteropServices
Imports System.Runtime.CompilerServices

<Assembly: InternalsVisibleTo("SharedCollections.Tests")> 

Public Class SharedDictionary(Of TKey, TValue)
  Implements IDictionary(Of TKey, TValue)
  Implements IDisposable

  Private _dict As IDictionary(Of TKey, TValue)
  Private _lockController As ILockStrategy
  Private _isDisposed As Boolean

  Public Sub New()
    Me.New(LockingStrategy.ReaderWriter)
  End Sub

  Public Sub New(ByVal strategy As LockingStrategy)
    _dict = New Dictionary(Of TKey, TValue)
    _lockController = LockingStrategyFacotry.Create(strategy)
  End Sub

  Friend ReadOnly Property BackingDictionary As IDictionary(Of TKey, TValue)
    Get
      Return _dict
    End Get
  End Property

  Public Function GetReadLock() As ISharedCollectionLock
    EnsureNotDisposed()
    Return _lockController.GetReadLock()
  End Function

  Public Function GetWriteLock() As ISharedCollectionLock
    EnsureNotDisposed()
    Return _lockController.GetWriteLock()
  End Function

  Private Sub EnsureReadAccess()
    If Not (_lockController.ThreadCanRead) Then
      Throw New ReadLockRequiredException()
    End If
  End Sub

  Private Sub EnsureWriteAccess()
    If Not _lockController.ThreadCanWrite Then
      Throw New WriteLockRequiredException()
    End If
  End Sub

  Public Function IEnumerable_GetEnumerator() As IEnumerator(Of KeyValuePair(Of TKey, TValue)) Implements IEnumerable _
                                                                                                (Of KeyValuePair _
                                                                                                (Of TKey, TValue)).GetEnumerator
    EnsureNotDisposed()
    EnsureReadAccess()

    'todo nothing ensures read lock is held for life of enumerator
    Return _dict.GetEnumerator()
  End Function

  Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
    Return IEnumerable_GetEnumerator()
  End Function

  Public Sub Add(ByVal item As KeyValuePair(Of TKey, TValue)) Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Add
    EnsureNotDisposed()
    EnsureWriteAccess()
    _dict.Add(item)
  End Sub

  Public Sub Clear() Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Clear
    EnsureNotDisposed()
    EnsureWriteAccess()
    _dict.Clear()
  End Sub

  Public Function Contains(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection _
                                                                                    (Of KeyValuePair(Of TKey, TValue)).Contains
    EnsureNotDisposed()
    EnsureReadAccess()
    Return _dict.Contains(item)
  End Function

  Public Sub CopyTo(ByVal array As KeyValuePair(Of TKey, TValue)(), ByVal arrayIndex As Integer) Implements ICollection _
                                                                                                  (Of KeyValuePair _
                                                                                                  (Of TKey, TValue)).CopyTo
    EnsureNotDisposed()
    EnsureReadAccess()
    _dict.CopyTo(array, arrayIndex)
  End Sub

  Public Function Remove(ByVal item As KeyValuePair(Of TKey, TValue)) As Boolean Implements ICollection(Of KeyValuePair _
                                                                                  (Of TKey, TValue)).Remove
    EnsureNotDisposed()
    EnsureWriteAccess()
    Return _dict.Remove(item)
  End Function

  Public ReadOnly Property Count() As Integer Implements ICollection(Of KeyValuePair(Of TKey, TValue)).Count
    Get
      EnsureNotDisposed()
      EnsureReadAccess()
      Return _dict.Count
    End Get
  End Property

  Public ReadOnly Property IsReadOnly() As Boolean Implements ICollection(Of KeyValuePair(Of TKey, TValue)).IsReadOnly
    Get
      EnsureNotDisposed()
      EnsureReadAccess()
      Return _dict.IsReadOnly
    End Get
  End Property

  Public Function ContainsKey(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).ContainsKey
    EnsureNotDisposed()
    EnsureReadAccess()
    Return _dict.ContainsKey(key)
  End Function

  Public Sub Add(ByVal key As TKey, ByVal value As TValue) Implements IDictionary(Of TKey, TValue).Add
    EnsureNotDisposed()
    EnsureWriteAccess()
    _dict.Add(key, value)
  End Sub

  Public Function Remove(ByVal key As TKey) As Boolean Implements IDictionary(Of TKey, TValue).Remove
    EnsureNotDisposed()
    EnsureWriteAccess()
    Return _dict.Remove(key)
  End Function

  Public Function TryGetValue(ByVal key As TKey, <Out()> ByRef value As TValue) As Boolean Implements IDictionary _
                                                                                           (Of TKey, TValue).TryGetValue
    EnsureNotDisposed()
    EnsureReadAccess()
    Return _dict.TryGetValue(key, value)
  End Function

  Default Public Property Item(ByVal key As TKey) As TValue Implements IDictionary(Of TKey, TValue).Item
    Get
      EnsureNotDisposed()
      EnsureReadAccess()
      Return _dict(key)
    End Get
    Set(ByVal value As TValue)
      EnsureNotDisposed()
      EnsureWriteAccess()
      _dict(key) = value
    End Set
  End Property

  Public ReadOnly Property Keys() As ICollection(Of TKey) Implements IDictionary(Of TKey, TValue).Keys
    Get
      EnsureNotDisposed()
      EnsureReadAccess()
      Return _dict.Keys
    End Get
  End Property

  Public ReadOnly Property Values() As ICollection(Of TValue) Implements IDictionary(Of TKey, TValue).Values
    Get
      EnsureNotDisposed()
      EnsureReadAccess()
      Return _dict.Values
    End Get
  End Property

  Private Sub EnsureNotDisposed()
    If _isDisposed Then
      Throw New ObjectDisposedException("SharedDictionary")
    End If
  End Sub

  Public Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)

    GC.SuppressFinalize(Me)
  End Sub

  Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    If Not _isDisposed Then
      If disposing Then
        'dispose managed resrources here
        _dict = Nothing
      End If

      'dispose unmanaged resrources here
      _lockController.Dispose()
      _lockController = Nothing
      _isDisposed = True
    End If
  End Sub

  Protected Overrides Sub Finalize()
    Dispose(False)
  End Sub
End Class